Astonishingly Easy Mapping with mapview



Exploratory spatial dataviz

with the mapview R package



Sharon Machlis at RLadies Cambridge March 2023

Who am I?

  • Director, editorial data & analytics at Foundry (publisher of Computerworld, InfoWorld, PCWorld, Macworld)
  • Author of InfoWorld’s Do More With R Series
  • Author, Practical R for Mass Communication & Journalism (CRC Press)

Who aren’t I?

Who aren’t I?

A design expert

Screen shot of my Astonishingly Easy Mapping with mapview article

Data

England and Wales 2021 Census data downloaded from the fabulous Census 2021 Explorer at ObservableHQ.com:

https://observablehq.com/@jwolondon/census-2021-explorer

Image of data download options including Geography, Variable, and Extra columns with a Save as CSV button

1. Import data files into R

(Pre-wrangled, script is in the GitHub repo)

library(pacman)
p_load(sf, mapview, dplyr, stringr)
mydata <- readRDS("data/census_data_2021.Rds")
glimpse(mydata)
Rows: 7,264
Columns: 10
$ code       <chr> "E02000001", "E02000002", "E02000003", "E02000004", "E02000…
$ population <int> 8583, 8281, 11542, 6640, 11082, 10162, 13250, 12863, 9485, …
$ areaName   <chr> "City of London 001", "Barking and Dagenham 001", "Barking …
$ region     <chr> "E12000007", "E12000007", "E12000007", "E12000007", "E12000…
$ popDensity <dbl> 2975.0, 3831.0, 5389.7, 2663.6, 9327.5, 5854.4, 9331.6, 133…
$ dep0       <int> 2937, 954, 1649, 1014, 1544, 1335, 1546, 1578, 1219, 904, 1…
$ dep1       <int> 1547, 1085, 1467, 829, 1252, 1320, 1729, 1600, 1241, 912, 1…
$ dep2       <int> 347, 613, 667, 365, 620, 739, 1017, 811, 634, 433, 605, 692…
$ dep3       <int> 75, 194, 145, 98, 201, 265, 306, 252, 211, 90, 134, 245, 20…
$ dep4       <int> 8, 24, 2, 3, 13, 16, 26, 13, 22, 13, 9, 12, 13, 21, 8, 10, …

2. Read spatial file into R with sf’s st_read()

mygeo <- st_read("data/msoas.json", quiet = TRUE)

Quick plot to see what we have

mapview(mygeo)

3. Join data with geography

mysf <- left_join(mygeo, mydata, by = "code")
glimpse(mysf)
Rows: 7,264
Columns: 12
$ id         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ code       <chr> "E02000001", "E02000002", "E02000003", "E02000004", "E02000…
$ population <int> 8583, 8281, 11542, 6640, 11082, 10162, 13250, 12863, 9485, …
$ areaName   <chr> "City of London 001", "Barking and Dagenham 001", "Barking …
$ region     <chr> "E12000007", "E12000007", "E12000007", "E12000007", "E12000…
$ popDensity <dbl> 2975.0, 3831.0, 5389.7, 2663.6, 9327.5, 5854.4, 9331.6, 133…
$ dep0       <int> 2937, 954, 1649, 1014, 1544, 1335, 1546, 1578, 1219, 904, 1…
$ dep1       <int> 1547, 1085, 1467, 829, 1252, 1320, 1729, 1600, 1241, 912, 1…
$ dep2       <int> 347, 613, 667, 365, 620, 739, 1017, 811, 634, 433, 605, 695…
$ dep3       <int> 75, 194, 145, 98, 201, 265, 306, 252, 211, 90, 134, 225, 24…
$ dep4       <int> 8, 24, 2, 3, 13, 16, 26, 13, 22, 13, 9, 15, 12, 13, 21, 8, …
$ geometry   <MULTIPOLYGON> MULTIPOLYGON (((532140.5 18..., MULTIPOLYGON (((54…

4. Plot the joined data

mapview(mysf)

Change the rollover display

The rollover displays the row names, so I’ll set new row names:

rownames(mysf) <- mysf$areaName
mapview(mysf)

Cambridge

Several dplyr verbs work on sf objects, including filter() to look at a portion of the data.

cambridge <- mysf |>
  filter(str_detect(areaName, "Cambridge"))
  mapview(cambridge)

Super easy choropleth maps

Color by one column’s value with zcol argument

mapview(cambridge, zcol = "population")

Add background tiles

mapview(cambridge, zcol = "population",
        map.types = c("CartoDB.Positron", "Esri.WorldImagery")
        )

Why no tiles? No CRS!

Coordinate reference system: How 3D globe is translated to a 2-D drawing. Step 1: These files use the British National Grid coordinates, a CRS of 27700. Set that.

st_crs(cambridge) <- 27700

Change the projection to system used by most map tiles

Most background map tiles use 4326.

st_crs(cambridge) <- 27700
cambridge <- st_transform(cambridge, 4326)
# cambridge <- st_transform(cambridge, "+init=epsg:27700", "+init=epsg:4326")

Add background tiles

mapview(cambridge, zcol = "population",
        map.types = c("CartoDB.Positron", "OpenStreetMap.Mapnik", "Esri.WorldImagery")
        )

Change layer opacity

mapview(cambridge, zcol = "population", 
        alpha.regions = 0, lwd = 3, color = "white",
        map.types = c("Esri.WorldImagery", "CartoDB.Positron", "OpenStreetMap.Mapnik")
        )

More tile options

https://leaflet-extras.github.io/leaflet-providers/preview/

Examples

Add more data layers

mapview(cambridge, zcol = "population", map.types = c("CartoDB.Positron", "OpenStreetMap.Mapnik", "Esri.WorldImagery"), alpha.regions = 1) +
mapview(cambridge, zcol = "popDensity", alpha.regions = 1) +
mapview(cambridge, zcol = "dep2", layer.name = "Econ depriv in 2 categories", alpha.regions = 1)  

Turn off legends with legend = false

mapview(cambridge, zcol = "population", map.types = c("CartoDB.Positron", "OpenStreetMap.Mapnik", "Esri.WorldImagery"), alpha.regions = 1, legend = FALSE) +
mapview(cambridge, zcol = "popDensity", legend = FALSE, alpha.regions = 1) +
mapview(cambridge, zcol = "dep2", laye.name = "Econ depriv in 2 categories", legend = FALSE, alpha.regions = 1)  

Change the default color palette

mapview(cambridge, zcol = "popDensity", legend = FALSE, 
        col.regions = rcartocolor::carto_pal(name = "SunsetDark"))

Side by side maps with slider!

no_deprivation <- mapview(cambridge, zcol = "dep0", map.types = c("CartoDB.Positron", "OpenStreetMap.Mapnik", "Esri.WorldImagery"), col.regions = RColorBrewer::brewer.pal(9, "Reds"),  layer.name = "No deprivation")
two_deprivation <- mapview(cambridge, zcol = "dep2", map.types = c("CartoDB.Positron", "OpenStreetMap.Mapnik", "Esri.WorldImagery"), col.regions = RColorBrewer::brewer.pal(9, "Reds"),  layer.name = "2 deprivations")
no_deprivation | two_deprivation

Add address search with leafem!

library(leafem)
mapview(cambridge, zcol = "population", legend = FALSE, 
  col.regions = rcartocolor::carto_pal(name = "SunsetDark")) %>%
  garnishMap(leaflet.extras::addSearchOSM)

Customize popups

my_popups <- str_glue("<strong>Area: {cambridge$areaName}</strong><br />Population Density: {cambridge$popDensity}<br />Total Population: {cambridge$population}") %>%
  lapply(htmltools::HTML)
mapview(cambridge, zcol = "popDensity", map.types = "CartoDB.Positron", popup = my_popups, label = my_popups)

Other great R mapping packages

  • tmap Almost as easy to use, static and interactive working environments, built-in customized binning
  • leaflet A bit more complicated to use but extreme control over look and feel
  • ggplot2 Not only for GIS of course, but useful mapping capabilities for those already familiar with the ecosystem

More on mapview

“Astonishingly easy mapping in R with mapview” - my InfoWorld article and video

Package creator Tim Appelhans’s tutorial on YouTube

Thanks!

Repo with Quarto file for these slides at https://github.com/smach/RLadiesCambridge

Sharon Machlis: smach@fosstodon.org on Mastodon